#include "gpio_pin.h"

void pin_mode(uint32_t pin, uint32_t mode)
{
    uint32_t port = (pin >> 4) & 0xF;
    uint32_t pin_num = pin & 0xF;
    GPIO_TypeDef *gpio = GPIO_PORT_BASE(port);

#if defined(SOC_SERIES_STM32F1)
    /* 使能时钟 */
    RCC->APB2ENR |= (1 << (port + 2));
    if (mode == PIN_MODE_OUTPUT)
    {
        if (pin_num < 8)
        {
            gpio->CRL &= ~(0xF << (pin_num * 4));
            gpio->CRL |=  (0x3 << (pin_num * 4)); // 50MHz 推挽输出
        }
        else
        {
            gpio->CRH &= ~(0xF << ((pin_num - 8) * 4));
            gpio->CRH |=  (0x3 << ((pin_num - 8) * 4));
        }
    }
    else // 输入
    {
        if (pin_num < 8)
        {
            gpio->CRL &= ~(0xF << (pin_num * 4));
            gpio->CRL |=  (0x4 << (pin_num * 4)); // 浮空输入
        }
        else
        {
            gpio->CRH &= ~(0xF << ((pin_num - 8) * 4));
            gpio->CRH |=  (0x4 << ((pin_num - 8) * 4));
        }
        if (mode == PIN_MODE_INPUT_PULLUP)
            gpio->ODR |= (1 << pin_num);
        else if (mode == PIN_MODE_INPUT_PULLDOWN)
            gpio->ODR &= ~(1 << pin_num);
    }
#elif defined(SOC_SERIES_STM32F4)
    /* 使能时钟 */
    RCC->AHB1ENR |= (1 << port);
    // 设置MODER
    gpio->MODER &= ~(0x3 << (pin_num * 2));
    if (mode == PIN_MODE_OUTPUT)
        gpio->MODER |=  (0x1 << (pin_num * 2)); // 输出
    else
        gpio->MODER |=  (0x0 << (pin_num * 2)); // 输入

    // 设置PUPDR
    gpio->PUPDR &= ~(0x3 << (pin_num * 2));
    if (mode == PIN_MODE_INPUT_PULLUP)
        gpio->PUPDR |= (0x1 << (pin_num * 2));
    else if (mode == PIN_MODE_INPUT_PULLDOWN)
        gpio->PUPDR |= (0x2 << (pin_num * 2));
#endif
}

void pin_set(uint32_t pin, uint8_t value)
{
    uint32_t port = (pin >> 4) & 0xF;
    uint32_t pin_num = pin & 0xF;
    GPIO_TypeDef *gpio = GPIO_PORT_BASE(port);

#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F4)
    if (value)
        gpio->BSRR = (1 << pin_num);
    else
        gpio->BSRR = (1 << (pin_num + 16));
#endif
}

uint8_t pin_get(uint32_t pin)
{
    uint32_t port = (pin >> 4) & 0xF;
    uint32_t pin_num = pin & 0xF;
    GPIO_TypeDef *gpio = GPIO_PORT_BASE(port);

#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F4)
    return (gpio->IDR & (1 << pin_num)) ? 1 : 0;
#else
    return 0;
#endif
}